//============================================================================
// ByteQueue.cs
//
// Copyright (c) 2005 U.S. TrailMaps, LLC
// THIS MATERIAL IS CONFIDENTIAL AND PROPRIETARY TO U.S. TRAILMAPS AND
// MAY NOT BE REPRODUCED, PUBLISHED OR DISCLOSED TO OTHERS WITHOUT COMPANY
// AUTHORIZATION.
//
// This work is derived from Waymex GPS Library for .Net version 2.7.9 source.
// Portions copyright (c) 2004-2005 Waymex IT Ltd
//============================================================================

using System;
using System.Globalization;

namespace Waymex.Gps.Garmin.Usb
{
	internal class ByteQueue
	{
		private const int _minimumGrow = 4;

		private byte[] _array;
		private int _growFactor;
		private int _head;
		private int _tail;
		private int _size;

		private ByteQueue(bool trash)
		{
		}

		public ByteQueue(int capacity) : this(capacity, 2f)
		{
		}

		public ByteQueue(int capacity, float growFactor)
		{
			if (capacity < 0)
				throw new ArgumentOutOfRangeException("capacity", "Non-negative number required.");
			if ((growFactor < 1) || (growFactor > 10))
				throw new ArgumentOutOfRangeException("growFactor", string.Format(CultureInfo.CurrentCulture, "Queue grow factor must be between {0} and {1}.", 1, 10));
			_array = new byte[capacity];
			_growFactor = (int)(growFactor * 100f);
		}

		public virtual int Count
		{
			get
			{
				return _size;
			}
		}

		private void SetCapacity(int capacity)
		{
			byte[] newArray = new byte[capacity];
			if (_size > 0)
			{
				if (_head < _tail)
					Array.Copy(_array, _head, newArray, 0, _size);
				else
				{
					Array.Copy(_array, _head, newArray, 0, (int)(_array.Length - _head));
					Array.Copy(_array, 0, newArray, (int)(_array.Length - _head), _tail);
				}
			}
			_array = newArray;
			_head = 0;
			_tail = _size;
		}

		public virtual void Clear()
		{
			if (_head < _tail)
				Array.Clear(_array, _head, _size);
			else
			{
				Array.Clear(_array, _head, _array.Length - _head);
				Array.Clear(_array, 0, _tail);
			}
			_head = 0;
			_tail = 0;
			_size = 0;
		}

		public virtual void Enqueue(byte value)
		{
			if (_size == _array.Length)
			{
				int newCapacity = (int)((_array.Length * _growFactor) / ((long)100));
				if (newCapacity < (_array.Length + _minimumGrow))
					newCapacity = _array.Length + _minimumGrow;
				SetCapacity(newCapacity);
			}
			_array[_tail] = value;
			_tail = (_tail + 1) % _array.Length;
			_size++;
		}

		public virtual byte Dequeue()
		{
			if (_size == 0)
				throw new InvalidOperationException("Queue empty.");
			byte value = _array[_head];
			_array[_head] = 0;
			_head = (_head + 1) % _array.Length;
			_size--;
			return value;
		}

		public virtual byte Peek()
		{
			if (_size == 0)
				throw new InvalidOperationException("Queue empty.");
			return _array[_head];
		}

		public static ByteQueue Synchronized(ByteQueue queue)
		{
			if (queue == null)
			{
				throw new ArgumentNullException("queue");
			}
			return new ByteQueue.SyncByteQueue(queue);
		}

		private class SyncByteQueue : ByteQueue
		{
			private ByteQueue _queue;

			internal SyncByteQueue(ByteQueue queue) : base(false)
			{
				this._queue = queue;
			}

			public override int Count
			{
				get
				{
					lock (this)
					{
						return _queue.Count;
					}
				}
			}

			public override void Clear()
			{
				lock (this)
				{
					_queue.Clear();
				}
			}

			public override void Enqueue(byte value)
			{
				lock (this)
				{
					_queue.Enqueue(value);
				}
			}

			public override byte Dequeue()
			{
				lock (this)
				{
					return _queue.Dequeue();
				}
			}

			public override byte Peek()
			{
				lock (this)
				{
					return _queue.Peek();
				}
			}
		}
	}
}
